package jmine.tec.web.servlet.rest.subscription;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import jmine.tec.di.type.TypeConverter;
import jmine.tec.di.type.TypeConvertionException;
import jmine.tec.di.type.TypeConvertionSystem;
import jmine.tec.subscription.Publisher;
import jmine.tec.subscription.PublisherManager;
import jmine.tec.subscription.SubscriptionManager;
import jmine.tec.subscription.SubscriptionSet;
import jmine.tec.subscription.SubscriptionSettings;
import jmine.tec.utils.UnmodifiableTuple;
import jmine.tec.web.servlet.rest.RestImplementor;
import jmine.tec.web.servlet.rest.exception.HttpErrorException;
import bancosys.tec.persist.dao.BeanNotFoundException;

/**
 * {@link RestImplementor} para servicos de Subscription
 * 
 * @author takeshi
 */
public class RestSubscriptionImplementor extends RestImplementor {
    private PublisherManager publisherManager;

    private SubscriptionManager subscriptionManager;

    private SubscriptionServletSupport subscriptionServletSupport;

    private TypeConvertionSystem typeConvertionSystem = new TypeConvertionSystem();

    /**
     * {@inheritDoc}
     */
    @Override
    public String getPreffix() {
        return "subscription";
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected Object get(HttpServletRequest request) throws BeanNotFoundException, HttpErrorException {
        SubscriptionSet set = this.subscriptionServletSupport.getSubscriptionSet(request);
        if (set == null) {
            return new HashMap<String, String>();
        }
        return this.subscriptionManager.getChanges(set);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected Object get(String identification, HttpServletRequest request) throws BeanNotFoundException, HttpErrorException {
        SubscriptionSet set = this.getSubscriptionSet(request);
        long token = Long.valueOf(identification);
        return this.subscriptionManager.getChanges(set, token);
    }

    /**
     * @param request {@link HttpServletRequest}
     * @return {@link SubscriptionSet}
     */
    private SubscriptionSet getSubscriptionSet(HttpServletRequest request) {
        return this.subscriptionServletSupport.getOrCreateSubscriptionSet(request);
    }

    /**
     * Faz a coercao dos parametros do request para o mapa
     * 
     * @param request {@link HttpServletRequest}
     * @param subscriptionSettingsParameters {@link Map}
     * @return {@link Map}
     * @throws TypeConvertionException e
     */
    private Map<String, Object> coerceTo(HttpServletRequest request, Map<String, Class<?>> subscriptionSettingsParameters)
            throws TypeConvertionException {
        Set<Entry<String, Class<?>>> entrySet = subscriptionSettingsParameters.entrySet();
        Map<String, Object> map = new HashMap<String, Object>(subscriptionSettingsParameters.size());
        for (Entry<String, Class<?>> entry : entrySet) {
            TypeConverter<?> converter = this.typeConvertionSystem.lookup(entry.getValue());
            final String theKey = entry.getKey();
            if (converter != null) {
                Object o = converter.convert(request.getParameter(theKey), entry.getValue());
                map.put(theKey, o);
            } else {
                map.put(theKey, request.getParameter(theKey));
            }
        }
        return map;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected Object post(String identification, HttpServletRequest request) throws BeanNotFoundException, HttpErrorException {
        Publisher<?> p = this.publisherManager.getPublisher(identification);
        if (p == null) {
            throw new HttpErrorException(HttpServletResponse.SC_NOT_FOUND, identification);
        }
        try {
            Map<String, Object> params = this.coerceTo(request, p.subscriptionSettingsParameters());
            SubscriptionSettings settings = p.newSubscriptionSettings(params);
            long token = this.subscriptionManager.addSubscription(this.getSubscriptionSet(request), p, settings).getSubscriptionToken();
            return UnmodifiableTuple.newTuple("newSubscription", token);
        } catch (TypeConvertionException e) {
            throw new HttpErrorException(HttpServletResponse.SC_PRECONDITION_FAILED, "");
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected Object delete(HttpServletRequest request) throws BeanNotFoundException, HttpErrorException {
        this.subscriptionServletSupport.cancelSubscription(request);
        return null;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected Object delete(String identification, HttpServletRequest request) throws BeanNotFoundException, HttpErrorException {
        long token = Long.valueOf(identification);
        SubscriptionSet set = this.getSubscriptionSet(request);
        this.subscriptionManager.unsubscribe(set, token);
        return null;
    }

    /**
     * @param publisherManager the publisherManager to set
     */
    public void setPublisherManager(PublisherManager publisherManager) {
        this.publisherManager = publisherManager;
    }

    /**
     * @param subscriptionManager the subscriptionManager to set
     */
    public void setSubscriptionManager(SubscriptionManager subscriptionManager) {
        this.subscriptionManager = subscriptionManager;
        this.subscriptionServletSupport = new SubscriptionServletSupport();
        this.subscriptionServletSupport.setSubscriptionManager(subscriptionManager);
    }

    /**
     * @return the typeConvertionSystem
     */
    public TypeConvertionSystem getTypeConvertionSystem() {
        return this.typeConvertionSystem;
    }

    /**
     * @param typeConvertionSystem the typeConvertionSystem to set
     */
    public void setTypeConvertionSystem(TypeConvertionSystem typeConvertionSystem) {
        this.typeConvertionSystem = typeConvertionSystem;
    }

}
